/*******************************************************************************
** 文件名称：gm_calendar.h
** 文件作用：通用日历操作
** 创建作者：Tom Free 付瑞彪
** 创建时间：2018-11-10
** 文件备注：日历相关操作，包括时间转换，时区转换，星期计算等
**
** 更新记录：
**          2018-11-10 -> 创建文件
**                                                          <Tom Free 付瑞彪>
**          2020-12-29 -> 修改代码部分命名，增加详细注释，增加时区设置
**                                                          <Tom Free 付瑞彪>
**          2021-01-08 -> 添加日期前后一天的计算和闰年判断
**                                                          <Tom Free 付瑞彪>
**
**          Copyright (c) 2018-2021 付瑞彪 All Rights Reserved
**
**       1 Tab == 4 Spaces     UTF-8     ANSI C Language(C99)
*******************************************************************************/
#ifndef __GM_CALENDER_H__
#define __GM_CALENDER_H__

#include "gm_calendar_cfg.h"
#include "stdint.h"
#ifndef __cplusplus
#include "stdbool.h"
#endif

/* 公历日期 */
typedef struct _gm_solar_t
{
    uint16_t year;      /* 年，>=1970 */
    uint8_t  month;     /* 月，1-12 */
    uint8_t  day;       /* 日，1-31，不同月份最大值不同 */
} gm_solar_t;

/* 时间类型定义 */
typedef struct _gm_time_t
{
    uint16_t year;      /* 年，>=1970 */
    uint8_t  month;     /* 月，1-12 */
    uint8_t  day;       /* 日，1-31，不同月份最大值不同 */
    uint8_t  hour;      /* 时，0-11（12小时制）或0-23（24小时制） */
    uint8_t  minute;    /* 分，0-59 */
    uint8_t  second;    /* 秒，0-59 */
    uint8_t  week;      /* 星期，1-7，代表周一到周日 */
} gm_time_t;

/* 时间戳类型定义 */
typedef uint32_t gm_stamp_t;

#ifdef __cplusplus
extern "C" {
#endif

/*******************************************************************************
** 函数名称：gm_calendar_is_leap_year
** 函数作用：当前年是否是闰年
** 输入参数：year - 年
** 输出参数：是否是闰年
** 使用范例：gm_calendar_is_leap_year(2020);
** 函数备注：
*******************************************************************************/
bool gm_calendar_is_leap_year(uint16_t year);

/*******************************************************************************
** 函数名称：gm_calendar_set_time_zone
** 函数作用：设置参与计算的时区，采用UTC标识，东+西-，北京时间，东八区，UTC+8，输入8
** 输入参数：timezone - 时区，-12~12
** 输出参数：是否设置成功
** 使用范例：gm_calendar_set_time_zone(8);
** 函数备注：
*******************************************************************************/
bool gm_calendar_set_time_zone(int8_t timezone);

/*******************************************************************************
** 函数名称：gm_calendar_get_month_days
** 函数作用：获取月的天数
** 输入参数：year，month - 年月
** 输出参数：月份的天数
** 使用范例：uint8_t days = gm_calendar_get_month_days(2000, 2);
** 函数备注：
*******************************************************************************/
uint8_t gm_calendar_get_month_days(uint16_t year, uint8_t month);

/*******************************************************************************
** 函数名称：gm_calandar_calc_week_day
** 函数作用：根据日期计算星期
** 输入参数：year，month，day - 年月日
** 输出参数：星期，1~7
** 使用范例：uint8_t week_day = gm_calandar_calc_week_day(2000, 2, 28);
** 函数备注：年月日要合法
*******************************************************************************/
uint8_t gm_calandar_calc_week_day(uint16_t year, uint8_t month, uint8_t day);

/*******************************************************************************
** 函数名称：gm_calendar_calc_now_days
** 函数作用：计算某日期是那一年的第几天
** 输入参数：year，month，day - 年月日
** 输出参数：天数
** 使用范例：uint16_t days = gm_calendar_calc_now_days(2020, 2, 28);
** 函数备注：年月日要合法，否则输出为0
*******************************************************************************/
uint16_t gm_calendar_calc_now_days(uint16_t year, uint8_t month, uint8_t day);

/*******************************************************************************
** 函数名称：gm_calendar_calc_all_days
** 函数作用：计算某日期从1970年1月1日以来的总天数
** 输入参数：year，month，day - 年月日
** 输出参数：天数
** 使用范例：uint32_t days = gm_calendar_calc_all_days(2020, 2, 28);
** 函数备注：年月日要合法，否则输出为0
*******************************************************************************/
uint32_t gm_calendar_calc_all_days(uint16_t year, uint8_t month, uint8_t day);

/*******************************************************************************
** 函数名称：gm_calendar_all_days_to_solar
** 函数作用：通过1970年1月1日以来的总天数计算公历日期
** 输入参数：p_solar - 公历日期
**           days - 1970年1月1日以来的总天数
** 输出参数：无
** 使用范例：gm_calendar_all_days_to_solar(&solar, days);
** 函数备注：
*******************************************************************************/
void gm_calendar_all_days_to_solar(gm_solar_t *p_solar, uint16_t days);

/*******************************************************************************
** 函数名称：gm_calendar_check_time_validity
** 函数作用：判断时间合法性
** 输入参数：时间
** 输出参数：是否合法
** 使用范例：bool res = gm_calendar_check_time_validity(&time);
** 函数备注：年>=1970
*******************************************************************************/
bool gm_calendar_check_time_validity(gm_time_t* p_time);

/*******************************************************************************
** 函数名称：gm_calendar_time_to_stamp
** 函数作用：时间转时间戳
** 输入参数：p_time - 时间地址，输入的是对应时区时间，不是UTC时间
**           p_stamp - 时间戳数据地址
** 输出参数：是否计算成功
** 使用范例：bool res = gm_calendar_time_to_stamp(&stamp, &time);
** 函数备注：运算量较大，需小心使用，需保证指针合法
*******************************************************************************/
bool gm_calendar_time_to_stamp(gm_time_t* p_time, gm_stamp_t *p_stamp);

/*******************************************************************************
** 函数名称：gm_calendar_stamp_to_time
** 函数作用：时间戳转时间
** 输入参数：pstamp - 时间戳数据地址
**           ptime - 时间地址，输出的是对应时区时间，不是UTC时间
** 输出参数：是否计算成功
** 使用范例：bool res = gm_calendar_stamp_to_time(&stamp, &time);
** 函数备注：运算量较大，需小心使用，需保证指针合法
*******************************************************************************/
bool gm_calendar_stamp_to_time(gm_stamp_t *p_stamp, gm_time_t* p_time);

/*******************************************************************************
** 函数名称：gm_calendar_next_day
** 函数作用：计算当前公历的后一天
** 输入参数：p_solar - 公历日期
** 输出参数：是否计算成功
** 使用范例：bool res = gm_calendar_next_day(&solar);
** 函数备注：保证时间合法，否则返回错误
*******************************************************************************/
bool gm_calendar_next_day(gm_solar_t *p_solar);

/*******************************************************************************
** 函数名称：gm_calendar_prev_day
** 函数作用：计算当前公历的前一天
** 输入参数：p_solar - 公历日期
** 输出参数：是否计算成功
** 使用范例：bool res = gm_calendar_prev_day(&solar);
** 函数备注：保证时间合法，否则返回错误
*******************************************************************************/
bool gm_calendar_prev_day(gm_solar_t *p_solar);

#ifdef __cplusplus
}
#endif

#endif  /* __GM_CALENDER_H__ */
